// Tencent is pleased to support the open source community by making RapidJSON available.
// 
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed 
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 
// CONDITIONS OF ANY KIND, either express or implied. See the License for the 
// specific language governing permissions and limitations under the License.

#include "rapidjson.h"

#ifndef RAPIDJSON_STREAM_H_
#define RAPIDJSON_STREAM_H_

#include "encodings.h"

RAPIDJSON_NAMESPACE_BEGIN

///////////////////////////////////////////////////////////////////////////////
//  Stream

/*! \class rapidjson::Stream
    \brief Concept for reading and writing characters.

    For read-only stream, no need to implement PutBegin(), Put(), Flush() and PutEnd().

    For write-only stream, only need to implement Put() and Flush().

\code
concept Stream {
    typename Ch;    //!< Character type of the stream.

    //! Read the current character from stream without moving the read cursor.
    Ch Peek() const;

    //! Read the current character from stream and moving the read cursor to next character.
    Ch Take();

    //! Get the current read cursor.
    //! \return Number of characters read from start.
    size_t Tell();

    //! Begin writing operation at the current read pointer.
    //! \return The begin writer pointer.
    Ch* PutBegin();

    //! Write a character.
    void Put(Ch c);

    //! Flush the buffer.
    void Flush();

    //! End the writing operation.
    //! \param begin The begin write pointer returned by PutBegin().
    //! \return Number of characters written.
    size_t PutEnd(Ch* begin);
}
\endcode
*/

//! Provides additional information for stream.
/*!
    By using traits pattern, this type provides a default configuration for stream.
    For custom stream, this type can be specialized for other configuration.
    See TEST(Reader, CustomStringStream) in readertest.cpp for example.
*/
    template<typename Stream>
    struct StreamTraits {
        //! Whether to make local copy of stream for optimization during parsing.
        /*!
            By default, for safety, streams do not use local copy optimization.
            Stream that can be copied fast should specialize this, like StreamTraits<StringStream>.
        */
        enum {
            copyOptimization = 0
        };
    };

//! Reserve n characters for writing to a stream.
    template<typename Stream>
    inline void PutReserve(Stream &stream, size_t count) {
        (void) stream;
        (void) count;
    }

//! Write character to a stream, presuming buffer is reserved.
    template<typename Stream>
    inline void PutUnsafe(Stream &stream, typename Stream::Ch c) {
        stream.Put(c);
    }

//! Put N copies of a character to a stream.
    template<typename Stream, typename Ch>
    inline void PutN(Stream &stream, Ch c, size_t n) {
        PutReserve(stream, n);
        for (size_t i = 0; i < n; i++)
            PutUnsafe(stream, c);
    }

///////////////////////////////////////////////////////////////////////////////
// StringStream

//! Read-only string stream.
/*! \note implements Stream concept
*/
    template<typename Encoding>
    struct GenericStringStream {
        typedef typename Encoding::Ch Ch;

        GenericStringStream(const Ch *src) : src_(src), head_(src) {}

        Ch Peek() const { return *src_; }

        Ch Take() { return *src_++; }

        size_t Tell() const { return static_cast<size_t>(src_ - head_); }

        Ch *PutBegin() {
            RAPIDJSON_ASSERT(false);
            return 0;
        }

        void Put(Ch) { RAPIDJSON_ASSERT(false); }

        void Flush() { RAPIDJSON_ASSERT(false); }

        size_t PutEnd(Ch *) {
            RAPIDJSON_ASSERT(false);
            return 0;
        }

        const Ch *src_;     //!< Current read position.
        const Ch *head_;    //!< Original head of the string.
    };

    template<typename Encoding>
    struct StreamTraits<GenericStringStream<Encoding> > {
        enum {
            copyOptimization = 1
        };
    };

//! String stream with UTF8 encoding.
    typedef GenericStringStream<UTF8<> > StringStream;

///////////////////////////////////////////////////////////////////////////////
// InsituStringStream

//! A read-write string stream.
/*! This string stream is particularly designed for in-situ parsing.
    \note implements Stream concept
*/
    template<typename Encoding>
    struct GenericInsituStringStream {
        typedef typename Encoding::Ch Ch;

        GenericInsituStringStream(Ch *src) : src_(src), dst_(0), head_(src) {}

        // Read
        Ch Peek() { return *src_; }

        Ch Take() { return *src_++; }

        size_t Tell() { return static_cast<size_t>(src_ - head_); }

        // Write
        void Put(Ch c) {
            RAPIDJSON_ASSERT(dst_ != 0);
            *dst_++ = c;
        }

        Ch *PutBegin() { return dst_ = src_; }

        size_t PutEnd(Ch *begin) { return static_cast<size_t>(dst_ - begin); }

        void Flush() {}

        Ch *Push(size_t count) {
            Ch *begin = dst_;
            dst_ += count;
            return begin;
        }

        void Pop(size_t count) { dst_ -= count; }

        Ch *src_;
        Ch *dst_;
        Ch *head_;
    };

    template<typename Encoding>
    struct StreamTraits<GenericInsituStringStream<Encoding> > {
        enum {
            copyOptimization = 1
        };
    };

//! Insitu string stream with UTF8 encoding.
    typedef GenericInsituStringStream<UTF8<> > InsituStringStream;

RAPIDJSON_NAMESPACE_END

#endif // RAPIDJSON_STREAM_H_
